//
//  CS_AccompanyManager.swift
//  NewSS
//
//  Created by 杨馥瑞 on 2021/11/14.
//

import Foundation

class CS_AccompanyManager: NSObject {
    
    static let AccompantDirectory = "accompany"
    static let kRetryTimes = 3
    
    private(set) var recordedMusics = [CS_MusicModel]()
    private var _historyFilePath = ""
    private var _accompanyStorage: IKSmallFileStorage?
    private var _retryTimes = 0
    private var _trackIndex: [Int : CS_MusicModel]
    private var isBusy = false
    
    static let shardInstance = CS_AccompanyManager()
    private override init() {
        func clearCache() {
            DispatchQueue.once {
                // 删除之前缓存的加密音乐文件，killapp然后启动
                CS_AccompanyManager.clearCacheUncryptMusic()
                // 版本升级到4.0.6，删除之前所有资源文件
                CS_AccompanyManager.clearUnencryptToFileIfNeed()
            }
        }
        clearCache()
        _historyFilePath = CS_FileManager.filePathInDataDir(inLibrary: "recordedTracks.plist")
        _accompanyStorage = IKSmallFileStorage.init(key: CS_AccompanyManager.AccompantDirectory)
        _trackIndex = [Int : CS_MusicModel]()
        super.init()
        _accompanyStorage?.delegate = self
        _loadHistoryFile()
    }
    
    @discardableResult
    static func decryptFile(_ encryptedFile: String, toFile decryptedFile: String) -> Bool {
        let key = "741e4406a0babe12d004679d07e9b8b662100ac879635207ae8947b34e37f6f2"
        let iv = "94a21f00f3e10253b32cb1da074f28aa"
        let result = IKMusicFileCrypto.decryptFile(encryptedFile, toFile: decryptedFile, key: key, iv: iv)
        CJAssert((CS_FileManager.fileSize(atPath: decryptedFile) != 0), "uncrypt file size is zero")
        return result
    }
    
    func recordMusic(_ music: CS_MusicModel) {
        guard !music.isLocal else { return }
        recordedMusics = recordedMusics.filter { $0.musicTrackId != music.musicTrackId }
        recordedMusics.insert(music, at: 0)
        _trackIndex[music.musicTrackId] = music
        _saveToFile()
        // 下载歌曲
        guard let _accompanyStorage = _accompanyStorage else {
            return
        }
        if !_accompanyStorage.isNotSyncing() {
            isBusy = true
        }
        else {
            _retryTimes = 0
            _syncMusics()
        }
    }
    
    func deleteMusics(_ musicList: [CS_MusicModel]) {
        musicList.forEach { deleteMusic($0) }
    }
    
    /// 删除逻辑数据同时删除音频资源
    func deleteMusic(_ music: CS_MusicModel) {
        guard !music.isLocal else { return }
        recordedMusics = recordedMusics.filter { $0.musicTrackId != music.musicTrackId }
        _trackIndex.removeValue(forKey: music.musicTrackId)
        // 删除音频资源
        do {
            try FileManager.default.removeItem(atPath: getAudioLocalPath(from: music))
        } catch {
            CJLog("音乐 - 删除音频资源出错 \(error)")
        }
        _saveToFile()
    }
    
    func containsMusic(_ music: CS_MusicModel) -> Bool {
        guard !music.isLocal else { return false }
        return _trackIndex.keys.contains(music.musicTrackId)
    }
    
    func isMusicDownload(_ music: CS_MusicModel) -> Bool {
        guard !music.isLocal else { return false }
        guard let _accompanyStorage = _accompanyStorage else { return false }
        let audioUrl = CJUrlManager.shared.fullMusicTrackUrl(music.properUrlForUsed)
        let progress = _accompanyStorage.singleFileProgress(audioUrl)
        return progress >= 1 && containsMusic(music)
    }
    
    // 迁移77爱玩
    func isMusicDownloadURL(_ url: String) -> Bool {
        return false
    }
    
    func getMusicDownloadProgress(_ music: CS_MusicModel) -> CGFloat {
        guard let _accompanyStorage = _accompanyStorage else { return 0 }
        let audioUrl = CJUrlManager.shared.fullMusicTrackUrl(music.properUrlForUsed)
        return CGFloat(_accompanyStorage.singleFileProgress(audioUrl))
    }
    
    // 迁移77爱玩
    func getMusicDownloadProgressURL(_ url: String) -> CGFloat {
        return 0
    }
    
    func sync() {
        _retryTimes = 0
        _syncMusics()
    }
    
    func getAudioLocalPath(from music: CS_MusicModel) -> String {
        if music.audioEncryptUrl.isEmpty {
            return getUnEncryptAudioUrl(music.audioUrl)
        }
        else {
            return getEncryptAudioUrl(music.audioEncryptUrl)
        }
    }
    
    func getUnEncryptAudioUrl(_ url: String) -> String {
        let fullUrl = CJUrlManager.shared.fullMusicTrackUrl(url)
        return _accompanyStorage?.localFilePath(forUrl: fullUrl) ?? ""
    }
    
    func getEncryptAudioUrl(_ url: String) -> String {
        let audioUrl = CJUrlManager.shared.fullMusicTrackUrl(url)
        guard !audioUrl.isEmpty, let encryptURL = _accompanyStorage?.localFilePath(forUrl: audioUrl) else { return "" }
        let unEncryptFilePath = CS_AccompanyManager.cacheUncryptUrlDir().appendingPathComponent(encryptURL.lastPathComponent)
        if !FileManager.default.fileExists(atPath: unEncryptFilePath) {
            CS_AccompanyManager.decryptFile(encryptURL, toFile: unEncryptFilePath)
        }
        return unEncryptFilePath
    }
    
    // MARK: - 私有方法
    
    private func _loadHistoryFile() {
        guard let jsonMusics = NSArray(contentsOfFile: _historyFilePath) as? [[String : Any]] else { return }
        var musics = [CS_MusicModel]()
        for musicDic in jsonMusics {
            if let music = CS_MusicModel.getMusicModelWithDict(musicDic) {
                musics.append(music)
                _trackIndex[music.musicTrackId] = music
            }
        }
        recordedMusics = musics
    }
    
    private func _saveToFile() {
        guard let saveArray = recordedMusics.map({ $0.sourceDict }) as? NSArray else { return }
        DispatchQueue.global().async {
            let isSuccess = saveArray.write(toFile: self._historyFilePath, atomically: true)
            CJLog("save music tracks " + (isSuccess ? "ok" : "failed"))
        }
    }
    
    private func _syncMusics() {
        guard let _accompanyStorage = _accompanyStorage, _accompanyStorage.isNotSyncing() else {
            return
        }
        var urls = [String]()
        recordedMusics.forEach { music in
            let audioUrl = CJUrlManager.shared.fullMusicTrackUrl(music.properUrlForUsed)
            if !audioUrl.isEmpty {
                urls.append(audioUrl)
            }
        }
        isBusy = false
        _accompanyStorage.asyncDownload(urls)
    }

}

extension CS_AccompanyManager: IKSmallFileStorageDelegate {
    
    func didFinishWithError(_ stat: ME_SMALL_STORAGE_STATUS) {
        if stat != SMALL_STORAGE_STATUS_ALL && _retryTimes < CS_AccompanyManager.kRetryTimes {
            _retryTimes += 1
            _syncMusics()
        }
        else {
            if isBusy {
                _retryTimes = 0
                _syncMusics()
            }
        }
    }
    
}

// MARK: - FilePath

extension CS_AccompanyManager {
    
    /// 用于存储解加密的音乐url
    static func cacheUncryptUrlDir() -> String {
        guard let result = CS_FileManager.filePathInDataDir(inLibrary: "recordedTracksUnCrypt") else { return "" }
        if !CS_FileManager.isFileExists(result) {
            do {
                try FileManager.default.createDirectory(atPath: result, withIntermediateDirectories: true, attributes: nil)
            } catch {
                CJLog("音乐 - create recordedTracksUnCrypt error \(error)")
            }
        }
        return result
    }
    
    @discardableResult
    static func clearCacheUncryptMusic() -> Bool {
        let fileDir = cacheUncryptUrlDir()
        if FileManager.default.fileExists(atPath: fileDir) {
            // 先删除文件夹
            var result = false
            do {
                try FileManager.default.removeItem(atPath: fileDir)
                result = true
            } catch {
                return result
            }
            // 创建一个空文件夹
            do {
                try FileManager.default.createDirectory(atPath: fileDir, withIntermediateDirectories: true, attributes: nil)
            } catch {
                CJLog("音乐 - create recordedTracksUnCrypt error \(error)")
            }
            return result
        }
        return false
    }
    
    /// 存储、音乐下载类信息
    static func pathForStoreDownload() -> String {
        let dirPath = "download/\(AccompantDirectory)"
        return CS_FileManager.dirPath(inTmp: dirPath)
    }
    
    /// 删除未清除缓存数据
    static func clearUnencryptToFileIfNeed() {
        var needupdate = false
        let kMusicUpdateToEncryptVersion = "kMusicUpdateToEncryptVersion"
        if (UserDefaults.standard.object(forKey: kMusicUpdateToEncryptVersion) == nil) {
            needupdate = true
            if let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
                UserDefaults.standard.set(object: appVersion, forKey: kMusicUpdateToEncryptVersion)
            }
        }
        if !needupdate {
            return
        }
        guard let recordList = CS_FileManager.filePathInDataDir(inLibrary: "recordedTracks.plist") else {
            CJLog("recordedTracks.plist filePathInDataDir error")
            return
        }
        do {
            try FileManager.default.removeItem(atPath: recordList)
        } catch {
            CJLog("remove recordedTracks error \(error)")
        }
        do {
            // 删除4.0.5以前下载版本数据
            try FileManager.default.removeItem(atPath: pathForStoreDownload())
        } catch {
            CJLog("remove pathForStoreDownload error \(error)")
        }
    }
    
}
